package jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Statement执行SQL语句时, 可能因为拼接SQL语句导致SQL语句的语义改变(参考UserLogin.java)
 * 出现SQL遭到注入攻击的风险
 *
 * 为了解决这个问题，我们转为使用:PreparedStatement
 * 使用它可以执行预编译SQL语句，将所有待拼接的值先用"?"代替，将SQL语义明确下来发送给数据库
 * 之后再将需要的值传给数据库去执行。
 * 由于是先将预编译SQL发送给数据库，此时数据库会根据预编译SQL生成执行计划(语义定死了)，之后
 * 再传过去的内容数据库仅会将它当作"值"看待，而不会发生语义的变化。
 */
public class JDBCDemo07 {
    public static void main(String[] args) {
        /*
            使用预编译SQL语句来避免SQL注入攻击风险
         */
        UserInfo login = InputUtil.getInputObject(new UserInfo(), "欢迎登录", "login");
        try {
            Connection connection = DBUtil.getConnection();
            /*
                在预编译SQL中"?"仅能代表"值"

                SELECT username,password,nickname
                FROM userinfo
                WHERE username=? AND password=?;
             */
            String sql = "SELECT username,password,nickname\n" +
                         "FROM userinfo\n" +
                         "WHERE username=? AND password=?;";
            //创建预编译SQL语句执行对象时，要先将预编译SQL发送给数据库来确定语义
            //意味着数据库会先根据该SQL生成执行计划，但并没有马上执行，因为还缺少"值"
            PreparedStatement ps = connection.prepareStatement(sql);
            //通过PreparedStatement设置预编译SQL中各个"?"的值
            //针对?对应值的类型调用对应的set方法
            //这样传入的字符串参数就只是"值",不会再与sql预编译语句拼接
            ps.setString(1,login.getUsername());
            ps.setString(2,login.getPassword());
            //ps调用执行方法时不必再传入sql
            ResultSet rs = ps.executeQuery();
            if (rs.next())
                System.out.println("登陆成功, 欢迎你, "+rs.getString("nickname"));
            else
                System.out.println("登陆失败, 用户名或密码错误");

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
